using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Collections;
using System.ComponentModel;

namespace Pavilion
{
    public class ContainerNode : NodeBase
    {
        public event Action<NodeBase> NodeAdded, NodeRemoved;
        public event Action<ContainerNode, Link> LinkAdded;

        public EntryNode EntryNode { get; private set; }
        public NotifiableCollection<NodeBase> NodeCollection { get { return nodeDictionary.ValueCollection; } }
        public List<Link> LinkCollection { get; private set; }

        private NotifiableDictionary<string, NodeBase> nodeDictionary;

        public ContainerNode(string name) : base()
        {
            nodeDictionary = new NotifiableDictionary<string, NodeBase>();
            LinkCollection = new List<Link>();
            EntryNode = new EntryNode();
            
            AddNode(EntryNode);

            this.Name = name;
        }

        public override void Dispose()
        {
            base.Dispose();
            RemoveAllNodes();
            EntryNode = null;
        }

        public override NodeBase Clone()
        {
            ContainerNode containerNode = new ContainerNode(Name);
            base.InsertParentContent(containerNode);

            foreach (NodeBase node in this.NodeCollection)
            {
                if (node is EntryNode)
                    continue;

                containerNode.AddNode(node.Clone());
            }

            foreach (Link link in LinkCollection)
            {
                List<ILock> lockCollection = new List<ILock>();

                foreach (ILock item in link.LockCollection)
                    lockCollection.Add(item.Clone());

                containerNode.AddLink(new Link(containerNode[link.StartingNode.Name], containerNode[link.EndingNode.Name], lockCollection, link.OpenCriteria));
            }

            return containerNode;
        }

        public Link LinkNode(string nodeA, string nodeB, IEnumerable<ILock> lockList, OpenCriteria openCriteria)
        {
            return LinkNode(this[nodeA], this[nodeB], lockList, openCriteria);
        }

        public Link LinkNode(NodeBase nodeA, NodeBase nodeB, IEnumerable<ILock> lockList, OpenCriteria openCriteria)
        {
            if (nodeA == nodeB)
                return null;

            return AddLink(new Link(nodeA, nodeB, lockList, openCriteria));
        }

        public Link AddLink(Link link)
        {
            link.Disposed += new EventHandler(LinkNode_Disposed);

            LinkCollection.Add(link);
            OnLinkAdded(link);

            return link;
        }

        /// <summary>
        /// Removes all the links from this node.
        /// </summary>
        /// <param name="node">The specific linked node to be removed.</param>
        public void RemoveLinksByNode(NodeBase node)
        {
            for (int i = LinkCollection.Count - 1; i >= 0; i--)
            {
                Link link = LinkCollection[i];

                if (link.StartingNode == node || link.EndingNode == node)
                    RemoveLink(link);
            }
        }

        public void RemoveLink(Link link)
        {
            link.Dispose();
        }

        public void AddNode(NodeBase node)
        {
            node.ParentContainer = this;
            nodeDictionary.Add(node.Name, node);
            node.NameChanged += new NameChangedDelegate(Node_NameChanged);

            OnNodeAdded(node);
        }

        public void RemoveNode(string nodeName)
        {
            if (nodeName == EntryNode.Name)
                return;

            NodeBase nodeToRemove = this[nodeName];
            RemoveLinksByNode(nodeToRemove);

            nodeToRemove.NameChanged -= new NameChangedDelegate(Node_NameChanged);
            nodeDictionary.Remove(nodeName);
            nodeToRemove.Dispose();

            OnNodeRemoved(nodeToRemove);
        }

        private void RemoveAllNodes()
        {
            Array array = nodeDictionary.ValueCollection.ToArray();

            foreach (NodeBase node in array)
                RemoveNode(node.Name);
        }

        public bool Contains(string nodeName)
        {
            foreach (NodeBase node in nodeDictionary.ValueCollection)
            {
                if (node.Name == nodeName)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Validates the container and all its inner Nodes. Returns true if no error is found.
        /// </summary>
        public bool Validate()
        {
            if (nodeDictionary.Count == 0)
                return false;

            foreach (NodeBase node in nodeDictionary.ValueCollection)
            {
                if (node.GetType() == typeof(ContainerNode))
                {
                    if (!((ContainerNode)node).Validate())
                        return false;
                }
            }
            return true;
        }

        public string GetUniqueName(string prefix)
        {
            return GameEngine.GetUniqueName(nodeDictionary.KeyCollection, prefix);
        }

        public override string ToString()
        {
            return Name;
        }

        public NodeBase this[string nodeName]
        {
            get { return nodeDictionary[nodeName]; }
        }

        private void OnNodeAdded(NodeBase node)
        {
            if (NodeAdded != null)
                NodeAdded(node);
        }

        private void OnNodeRemoved(NodeBase node)
        {
            if (NodeRemoved != null)
                NodeRemoved(node);
        }

        private void OnLinkAdded(Link link)
        {
            if (LinkAdded != null)
                LinkAdded(this, link);
        }

        private void Node_NameChanged(string oldName, string newName)
        {
            nodeDictionary.ReplaceKey(oldName, newName);
        }

        private void LinkNode_Disposed(object sender, EventArgs e)
        {
            Link link = (Link)sender;

            link.Disposed -= new EventHandler(LinkNode_Disposed);
            LinkCollection.Remove(link);
        }
    }
}